perm filename MCTRST.PUB[HAL,HE] blob
sn#133594 filedate 1974-12-04 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00008 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 .NEWSS CONTROL STRUCTURES
C00009 00003 .cob:NEWSSS COBEGIN-COEND
C00011 00004 .tsk:NEWSSS PARTIAL ORDERING OF SUBTASKS
C00015 00005 .evt:NEWSSS EVENTS: SIGNAL AND WAIT
C00018 00006 .NEWSSS STATEMENT CONDITION MONITORS
C00021 00007 .NEWSSS COMMENTS
C00026 00008 .NEWSSS PROCEDURES
C00033 ENDMK
C⊗;
.NEWSS CONTROL STRUCTURES
.NEWSSS TRADITIONAL STRUCTURES
AL has many of the traditional Algol control structures,
including %4statements%*, %4blocks%*, %4conditionals%*, and
%4loops%*. There are no %4jumps%* in AL, because they confuse the
flow analysis needed for maintaining planning values and because it
is possible to accomplish much without them.
We have already seen some applications of block structure.
More formally, a
%4block%* is a list of %4statements%*, separated by semicolons, and
surrounded by the reserved words %4BEGIN%* and %4END%*. The entire
block is treated syntactically as a statement; thus, its definition
is recursive. One particular kind of statement is the
%4declaration%*. We have already seen declarations for algebraic
variables. There are a few rules pertaining to variables and
declarations: Every variable must be declared at some point in the
program before it is used. Declarations may appear anywhere in a
block; there is no restriction that they must precede other
statements. The %4local block%* of a variable is the block defined
by the narrowest BEGIN-END pair which surrounds its declaration.
Blocks defined by narrower BEGIN-END pairs are called %4inner
blocks%*, and those defined by wider pairs are called %4global
blocks%*. The primary rule is that variables may only be referenced
in their own local or inner blocks. Another way to state the same
thing is that within any given block, it is only legal to refer to
variables declared locally or globally to that block.
Here is a simple example which demonstrates the other standard ALGOL
control structures:
.UNFILL
sample: BEGIN α{%4Meaningless example%*α}
SCALAR a, i;
a ← 2;
FOR i ← 1 STEP 1 UNTIL 10 DO a ← a*a;
.comt 12
α{This is very likely to cause arithmetic overflow!α}
.end
WHILE a > 0 DO
loop: BEGIN
a ← a - 1;
IF a < 5 THEN WRITE(a) ELSE WRITE(a-5)
END loop;
WRITE("Done")
END sample
.REFILL
Even though there is no jump instruction, there are labels; they
are useful during debugging, for naming condition
monitors, and for some other purposes which we will see later.
It is good practice to name blocks, and to repeat the name after the
closing END; this allows the compiler to check that the proper
BEGINs and ENDs match.
The %4FOR loop%* is quite traditional; it follows the form:
.UNFILL
FOR <s var> ← <s expr> STEP <s expr> UNTIL <s expr> DO <statement>
.REFILL
where <s var> stands for "(possibly dimensioned) scalar variable" and
<s expr> stands for "scalar expression of compatible dimension". The
initial value of the variable is the value of the first expression;
every time the statement is executed, its value is incremented by the
value of the second expression, and the process repeats until the
value exceeds that of the third expression. If the step size is
negative, the right things happen. A test is made before the first
iteration, so it is possible that the loop will not get executed at
all.
The %4WHILE loop%* is another means to control iteration. It syntax is this:
.UNFILL
WHILE <condition> DO <statement>
.REFILL
where the condition is some boolean expression involving one of the
operators <, >, ≤, ≥, =, and ≠. Boolean
expressions can be built up out of such arithmetic operators, the logical
connectives %4∧ (and), ∨ (or), ¬ (not)%*, and the logical constants
%4true%* and %4false%*.
The first check is made before the first
iteration; the statement is executed repeatedly until the condition fails.
The %4conditional statement%* has the form:
.unfill
IF <condition>
THEN <statement>
ELSE <statement>
.refill
The ELSE part is optional. The <condition>, which is just like the
condition in a WHILE statement, is evaluated; if it is %4true%*, the
THEN part will get executed; if it is %4false%*, the ELSE part (if there
is one) gets executed.
The %4conditional expression%* is much the same as the conditional
statement:
.unfill
IF <condition>
THEN <expression>
ELSE <expression>
.refill
This can be used whenever an expression is needed.
.cob:NEWSSS COBEGIN-COEND
In addition to traditional ALGOL structures, there are also
some additional ones for more sophisticated flow of control. The first such
construct is the %4COBEGIN-COEND%* pair, which brackets statements
whose execution is meant to occur independently. Each of the
statements within the %4simultaneous%* block will eventually get executed, but
there may be considerable overlap of execution. For example, while
one arm is moving, another statement can be computing; several arms
can also work at the same time. The termination of the
block occurs only when all of the statements in the scope of the
COBEGIN have terminated. Declarations should not be included as local
statements in the region of simultaneity. It is not particularly
useful to have simultaneous execution of a purely computational code;
the real reason for the COBEGIN construct is to allow simultaneous
independent manipulator control. Here is a simple example:
.UNFILL
swing: COBEGIN α{%4Wish to get all three arms to their rest positions.%*α}
MOVE yellow TO ypark;
MOVE blue TO bpark;
MOVE red α{%4we should have such an arm%*α} TO rpark;
COEND swing
.REFILL
.tsk:NEWSSS PARTIAL ORDERING OF SUBTASKS
An assembly task is often divided into subtasks which enjoy a
partial ordering with respect to the intended order of execution.
For example, consider
a task A which contains four subtasks, B, C, D, and E, of which B and
C must be done before D, and D must be done before E, but B and C
could be done in any order. It is possible in AL to leave the
ordering of the subtasks up to the compiler, which will try to
optimize the entire operation
with respect to total expected time and economy of motion.
For example,
.UNFILL
a: TASK BEGIN α{%4Sample of partial ordering on subtasks%*α}
b: BEGIN
<code for task B>
END b;
c: <code for task C>;
d_e: BEGIN α{%4both D and E%*α}
<code for task D>
<code for task E>
END d_e;
PREREQUISITE OF d_e IS c;
PREREQUISITE OF d_e IS b;
END
.REFILL
The words TASK BEGIN introduce a %4task block%*, which contains
a set of statements. The prerequisite statement
.unfill
PREREQUISITE OF <label 1> IS <label 2>
.refill
informs the compiler that the statement identified by <label 2> must
be done before the statement identified by <label 1>. One important
restriction is that both statements so named, as well as the prerequisite
statement itself, must all occur in the same TASK block.
The order in which the statements are performed is determined
only insofar as the prerequisite conditions demand. The compiler may
reorder them consistently with the preconditions, and may even execute
some of the statements simultaneously (as if there were a COBEGIN),
if this is feasible.
As can be expected, it is rather difficult for the compiler
to keep track of planning values in the vicinity of a TASKBEGIN. For
this reason, it is a good idea to make heavy use of the planning
value assignment statement (the one with the double arrow: "α←α←"; see
{ssref pvs})
to keep the compiler informed of what is intended to be true, both
within the partially ordered block and immediately afterwards.
.evt:NEWSSS EVENTS: SIGNAL AND WAIT
To achieve simultaneous coordinated motion, one uses a
special form of the move commands which will be discussed later.
However, some simple synchronization is possible within the context of
simultaneous execution. This is achieved by means of explicit
events, which can be signaled and awaited. Every different event
that the user wishes to use should be declared. For instance,
.UNFILL
EVENT e1, e2, e3 .
.REFILL
With each event is associated a count of how many times it has been signalled.
Initially, the count is 0, that is, no signals have appeared,
and no process is waiting. The statement
.UNFILL
SIGNAL e1
.REFILL
increments the count associated with event e1, and if the resulting
count is 0 or negative, one of those processes waiting for e1 is
released from its wait and readied for execution. The statement
.UNFILL
WAIT e1
.REFILL
decrements the count associated with event E1, and if the resulting
count is negative, the process issuing the WAIT is blocked from
continuing until a signal comes along. If the count is 0 or
positive, there is no waiting.
An example of the utility of this construct is inside a
simultaneous block, where one path of execution requires that the other
path has passed some milestone. Here is how such a use might appear:
.UNFILL
EVENT milestone;
COBEGIN α{%4Example of use of SIGNAL and WAIT%*α}
path1: BEGIN
<code before the critical point>
WAIT milestone;
<code after the critical point>
END path1;
path2: BEGIN
<code in preparation for the milestone>
SIGNAL milestone;
<code following the milestone>
END path2
COEND
.REFILL
.NEWSSS STATEMENT CONDITION MONITORS
We have already seen condition monitors in the context of motion
statements. The same construct is of general utility; most of
what was said before holds for the %4statement condition monitor%*
as well.
The conditions which may be tested in statement condition monitors
are principally events, since DURATION, SQUEEZE, and FORCE are
associated usually with a particular motion.
As is the case with motion condition monitors, the statement condition
monitors can be in two states: %4enabled%* and %4disabled%*. A monitor
becomes enabled when its defining statement is executed, and becomes
disabled when it triggers, is explicitly disabled by some other
statement, or its local block is exited. The same conventions as
have already been seen apply to the naming of condition monitors
and their explicit enabling and disabling.
Scope rules come into play regarding what condition monitors it
is permitted to enable or disable; the rule is that only condition
monitors defined locally or globally to a piece of code can be
touched by that code.
The word DEFER still
causes a condition monitor to be defined in an initially disabled
state.
When a block is exited, all monitors local to that block are disabled.
However, the block exit code will wait until the bodies of any triggered
monitors are completed before disabling any monitors, deallocating local
variables, or performing any of the other functions associated
with block termination. If the execution of one of these triggered
monitor bodies causes other monitors to trigger, then the block
exit will wait for those, also. Furthermore, block exit is treated
as an atomic process; all monitors are disabled at the same time.
The constructs CRITICAL and UNCRITICAL apply to statement condition
monitors just as they do to motion condition monitors.
.NEWSSS COMMENTS
The most standard way to insert comments in an AL program
is to surround them with curly brackets, as we have done in all
our examples so far. It is also legal to use
the word COMMENT before a comment, and to end it
by a semicolon. This type of comment may not contain any semicolon.
The user can optionally reset the comment delimiters
from "α{α}" to whatever she wishes, by means of a require statement
such as
.UNFILL
REQUIRE "α%α%" COMMENT_DELIMITERS .
.REFILL
which would cause the scanner to ignore any text between "α%" signs.
It is expected that this will seldom be needed.
.NEWSSS LABELS
%4Labels%* specify points in the program; they are useful for
naming condition monitors, and subtasks. It
also assists during debugging of programs.
To label a statement, preface it with an identifier followed by a colon.
Labels are not declared.
.UNFILL
foo: a ← a + 1;
.REFILL
.NEWSSS ABORT
Occasionally the user wishes to stipulate that if the program
ever reaches a particular point, something is hopelessly wrong. The
statement ABORT causes the runtime to stop all moving devices and
to terminate execution. The supervisor is informed of the halt, and
will inform the user. ABORT takes an optional string argument, which
is a message which will be given to the user if the ABORT statement
is executed. An example:
.UNFILL
ABORT ("I keep missing the hole!")
.REFILL
.NEWSSS OUTPUT
There are several ways that the user can request output
from AL to the console. As mentioned above, ABORT can print
a message during execution. There is another way to print a
message during execution, the WRITE statement, which
takes as arguments a list of variables and constants.
It is also legal to include a string constant in this list (there
are no string variables in AL). Formatting of output is automatic.
An example:
.UNFILL
WRITE ("I think that the pump is at ",PUMP)
.REFILL
Some pieces of code are only intended to work under
certain conditions of planning knowledge. Such code might
have a check to insure that its preconditions are met; if not,
it is proper to signal a compile-time error, with a message.
This is done with the PLAN ERROR statement, which optionally
takes a string argument, and which will halt compilation and print
the message. One of the options the supervisor will give the user
is to proceed as if no error had been encountered. Here is
an example:
.UNFILL
PLAN ERROR("Hey! You didn't attach the pump to the hand!")
.REFILL
A similar statement which merely prints its message but
does not halt compilation is the PLAN WRITE statement, which
behaves in all respects like the runtime WRITE statement in that
it can take variables and constants in its argument list, but where
variables are specified, the planning values will be printed. For
example:
.UNFILL
PLAN WRITE("Yellow arm should be at",yellow)
.REFILL
.NEWSSS PROCEDURES
AL has only a limited capacity for procedures. All
parameters to a procedure assume the planning value "undefined" at
the conclusion of a procedure call, except those which are declared
as VALUE parameters in the procedure heading, or those stated to be
UNCHANGED in the procedure call. There is no safeguard against the
accidental modification of "unchanged" parameters; to state
"unchanged" is entirely equivalent to an assertion that the parameter
has not changed its value during the execution of the procedure. The
declaration of a procedure is this:
.UNFILL
type PROCEDURE name (argument list) ,
.REFILL
where %4type%* is any data type (and is optional), and %*argument list%*
is a list of parameter names with their types. An example:
.UNFILL
DISTANCE SCALAR PROCEDURE lgth (FRAME f1, f2; VECTOR VALUE v1);
.REFILL
This declares that lgth is a procedure which returns a scalar, and
takes as arguments two frames and one vector. The vector is not
changed by the procedure.
To call such a procedure:
.UNFILL
DISTANCE SCALAR s1;
FRAME frob, hole;
VECTOR vect;
:
s1 ← lgth(frob, UNCHANGED hole, vect);
.REFILL
This further asserts that hole is not changed by the call.
It is a good idea to use the planning value assignment ("α←α←")
heavily at the start of a procedure body to
inform the compiler of the values to expect for the various
arguments. Remember that trajectories planned on the basis of highly
inaccurate planning values will not work well.
As a procedure is entered, all variables have planning value
"undefined". Globals may be accessed, but they also have undefined
initial planning value. All variables which have explicit or
implicit assignments within a procedure acquire the value
"undefined" at the point directly after the procedure call.
No modification of the affixment structure is allowed inside a
procedure. The compiler (often wrongly) assumes that there are no
affixments involving variables within a procedure; it requires an assertion
like
.UNFILL
ASSERT FORM(AFFIXED, f1, f2) .
.REFILL
to override this mistake. Affixments are discussed in {ssref aff}
and more general assertions in {ssref asr}.
There are four special types of procedure calls: A AL
program might wish to call a routine coded for the mini or a routine
coded for the timesharing machine.
Likewise, a program on the timesharing computer may wish to
control an AL program, or a routine on the mini may wish to request
some arm motion.
To achieve the first two cases, there exist %4external
procedures%* in AL. These are compiled into calls on either routines
in the mini or routines in the timesharing computer's runtime package. To declare
such a procedure:
.UNFILL
EXTERNAL MAXI FRAME PROCEDURE foo (FRAME a, b; VECTOR v) .
.REFILL
This declares the procedure foo to be a procedure resident
in the runtime "maxi" (that is, timesharing
computer) package, expected to return a frame value, and
taking as arguments two frames and a vector. Maxi procedures do not
have access to the actual variables sent, since copies are made;
therefore, all arguments to maxi procedures are considered to be
VALUE parameters.
It is possible to declare untyped (i.e., statement-like,
instead of expression-like) procedures as well. Replace "maxi" with
"mini" for procedures in the mini. Such procedures do have access
to values, and therefore parameters are not automatically considered
to be VALUE.
To achieve the second two cases, there exist %4internal
procedures%* in AL:
.UNFILL
INTERNAL FORCE VECTOR PROCEDURE baz (SCALAR s);
.REFILL
Internal procedures must be at the top
level of an AL program. A complete AL program is considered to be
an untyped procedure without parameters.